<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>多视频合并长音频工具</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/ffmpeg/0.11.6/ffmpeg.min.js"></script>
    <style>
      * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        font-family: 'SF Pro Display', 'PingFang SC', 'Helvetica Neue', Arial,
          sans-serif;
      }

      :root {
        --primary-bg: #e6e7ee;
        --secondary-bg: #ffffff;
        --shadow-light: #ffffff;
        --shadow-dark: #d1d9e6;
        --accent-color: #667eea;
        --accent-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        --success-color: #4ecdc4;
        --danger-color: #ff6b6b;
        --warning-color: #feca57;
        --text-primary: #2d3748;
        --text-secondary: #718096;
        --glass-bg: rgba(255, 255, 255, 0.25);
        --glass-border: rgba(255, 255, 255, 0.18);
      }

      body {
        background: var(--primary-bg);
        color: var(--text-primary);
        min-height: 100vh;
        padding: 20px;
        display: flex;
        flex-direction: column;
        align-items: center;
        transition: all 0.3s ease;
        animation: pageLoad 0.8s ease;
      }

      @keyframes pageLoad {
        from {
          opacity: 0;
          transform: translateY(20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .container {
        width: 100%;
        max-width: 1200px;
        background: var(--primary-bg);
        border-radius: 30px;
        padding: 40px;
        box-shadow: 20px 20px 60px var(--shadow-dark),
          -20px -20px 60px var(--shadow-light);
        margin-top: 20px;
        transition: all 0.3s ease;
        animation: containerFloat 1s ease 0.2s both;
      }

      @keyframes containerFloat {
        from {
          opacity: 0;
          transform: translateY(30px) scale(0.95);
        }
        to {
          opacity: 1;
          transform: translateY(0) scale(1);
        }
      }

      .container:hover {
        box-shadow: 25px 25px 70px var(--shadow-dark),
          -25px -25px 70px var(--shadow-light);
      }

      header {
        text-align: center;
        margin-bottom: 40px;
        padding: 30px;
        background: var(--primary-bg);
        border-radius: 25px;
        box-shadow: 10px 10px 20px var(--shadow-dark),
          -10px -10px 20px var(--shadow-light);
        animation: headerSlide 0.8s ease 0.4s both;
      }

      @keyframes headerSlide {
        from {
          opacity: 0;
          transform: translateY(-30px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      h1 {
        font-size: 3.2rem;
        margin-bottom: 15px;
        background: var(--accent-gradient);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        background-clip: text;
        font-weight: 700;
        animation: titleGlow 3s ease-in-out infinite alternate;
      }

      @keyframes titleGlow {
        from {
          filter: brightness(1);
        }
        to {
          filter: brightness(1.2);
        }
      }

      .subtitle {
        font-size: 1.3rem;
        color: var(--text-secondary);
        margin-bottom: 30px;
        font-weight: 500;
      }

      .audio-section {
        margin-bottom: 40px;
        animation: sectionSlide 0.8s ease 0.6s both;
      }

      @keyframes sectionSlide {
        from {
          opacity: 0;
          transform: translateX(-20px);
        }
        to {
          opacity: 1;
          transform: translateX(0);
        }
      }

      .section-title {
        font-size: 1.8rem;
        color: var(--accent-color);
        margin-bottom: 20px;
        font-weight: 600;
        display: flex;
        align-items: center;
      }

      .section-title::before {
        content: attr(data-icon);
        margin-right: 12px;
        font-size: 2rem;
        animation: iconBounce 2s ease-in-out infinite;
      }

      @keyframes iconBounce {
        0%,
        100% {
          transform: scale(1);
        }
        50% {
          transform: scale(1.1);
        }
      }

      .panel {
        background: var(--primary-bg);
        border-radius: 25px;
        padding: 30px;
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
        transition: all 0.3s ease;
        position: relative;
        overflow: hidden;
        margin-bottom: 30px;
      }

      .panel::before {
        content: '';
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(
          90deg,
          transparent,
          rgba(255, 255, 255, 0.1),
          transparent
        );
        transition: left 0.8s;
      }

      .panel:hover::before {
        left: 100%;
      }

      .panel:hover {
        transform: translateY(-5px);
        box-shadow: 16px 16px 32px var(--shadow-dark),
          -16px -16px 32px var(--shadow-light);
      }

      .file-upload {
        border: 3px dashed var(--accent-color);
        border-radius: 25px;
        padding: 50px 40px;
        text-align: center;
        transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
        margin-bottom: 25px;
        cursor: pointer;
        background: var(--primary-bg);
        box-shadow: inset 8px 8px 16px var(--shadow-dark),
          inset -8px -8px 16px var(--shadow-light);
        position: relative;
        overflow: hidden;
      }

      .file-upload:hover {
        border-color: var(--success-color);
        transform: translateY(-5px) scale(1.02);
        box-shadow: inset 10px 10px 20px var(--shadow-dark),
          inset -10px -10px 20px var(--shadow-light),
          0 15px 40px rgba(102, 126, 234, 0.25), 0 5px 15px rgba(0, 0, 0, 0.1);
      }

      .file-upload.dragover {
        border-color: var(--success-color);
        background: rgba(78, 205, 196, 0.05);
        transform: scale(1.05);
        box-shadow: inset 12px 12px 24px var(--shadow-dark),
          inset -12px -12px 24px var(--shadow-light),
          0 20px 50px rgba(78, 205, 196, 0.3);
        animation: dragPulse 1s ease-in-out infinite;
      }

      @keyframes dragPulse {
        0%,
        100% {
          border-color: var(--success-color);
        }
        50% {
          border-color: var(--accent-color);
        }
      }

      .file-upload-content {
        position: relative;
        z-index: 1;
      }

      .file-upload-icon {
        font-size: 3.5rem;
        margin-bottom: 20px;
        display: block;
        animation: iconFloat 3s ease-in-out infinite;
        filter: drop-shadow(0 4px 8px rgba(102, 126, 234, 0.2));
      }

      @keyframes iconFloat {
        0%,
        100% {
          transform: translateY(0);
        }
        50% {
          transform: translateY(-10px);
        }
      }

      .file-upload-text {
        font-size: 1.4rem;
        font-weight: 600;
        color: var(--text-primary);
        margin-bottom: 10px;
        background: var(--accent-gradient);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        background-clip: text;
      }

      .file-upload-subtext {
        font-size: 1.1rem;
        color: var(--text-secondary);
        margin-bottom: 20px;
        font-weight: 500;
      }

      .file-upload-formats {
        font-size: 0.9rem;
        color: var(--accent-color);
        font-weight: 500;
        padding: 8px 16px;
        background: rgba(102, 126, 234, 0.1);
        border-radius: 20px;
        display: inline-block;
        margin-bottom: 20px;
      }

      .file-upload input[type='file'] {
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        opacity: 0;
        cursor: pointer;
        z-index: 2;
      }

      .video-list {
        margin-top: 20px;
      }

      .video-item {
        background: var(--primary-bg);
        border-radius: 20px;
        padding: 20px;
        margin-bottom: 20px;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
        transition: all 0.3s ease;
      }

      .video-item:hover {
        transform: translateY(-3px);
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
      }

      .video-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 15px;
      }

      .video-name {
        font-size: 1.1rem;
        font-weight: 600;
        color: var(--text-primary);
        flex: 1;
        margin-right: 15px;
      }

      .video-controls {
        display: flex;
        align-items: center;
        gap: 15px;
      }

      .duration-input {
        display: flex;
        align-items: center;
        gap: 8px;
      }

      .duration-input label {
        font-size: 0.9rem;
        color: var(--text-secondary);
        font-weight: 500;
      }

      .duration-input input {
        width: 80px;
        padding: 8px 12px;
        border: none;
        border-radius: 10px;
        background: var(--primary-bg);
        box-shadow: inset 4px 4px 8px var(--shadow-dark),
          inset -4px -4px 8px var(--shadow-light);
        font-size: 0.9rem;
        color: var(--text-primary);
        text-align: center;
      }

      .duration-input input:focus {
        outline: none;
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light),
          0 0 0 2px var(--accent-color);
      }

      .remove-btn {
        background: var(--danger-color);
        color: white;
        border: none;
        padding: 8px 12px;
        border-radius: 10px;
        cursor: pointer;
        font-size: 0.8rem;
        font-weight: 600;
        transition: all 0.3s ease;
        box-shadow: 4px 4px 8px var(--shadow-dark),
          -4px -4px 8px var(--shadow-light);
      }

      .remove-btn:hover {
        transform: translateY(-2px);
        box-shadow: 6px 6px 12px var(--shadow-dark),
          -6px -6px 12px var(--shadow-light);
      }

      .video-preview {
        width: 100%;
        max-height: 200px;
        border-radius: 15px;
        margin-top: 10px;
      }

      .audio-preview {
        width: 100%;
        margin-top: 15px;
      }

      .add-video-btn {
        background: var(--success-color);
        color: white;
        border: none;
        padding: 12px 24px;
        border-radius: 15px;
        cursor: pointer;
        font-size: 1rem;
        font-weight: 600;
        margin-top: 20px;
        transition: all 0.3s ease;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
      }

      .add-video-btn:hover {
        transform: translateY(-3px);
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
      }

      .btn-primary {
        background: var(--accent-gradient);
        color: white;
        padding: 16px 32px;
        font-size: 1.1rem;
        border: none;
        border-radius: 15px;
        cursor: pointer;
        font-weight: 600;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2);
        margin: 20px auto;
        display: block;
        transition: all 0.3s ease;
      }

      .btn-primary:hover {
        transform: translateY(-3px);
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2),
          0 0 30px rgba(102, 126, 234, 0.3);
      }

      .btn-primary:active {
        transform: translateY(0);
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light);
      }

      .btn-primary:disabled {
        opacity: 0.6;
        cursor: not-allowed;
        transform: none;
        box-shadow: 4px 4px 8px var(--shadow-dark),
          -4px -4px 8px var(--shadow-light);
      }

      .progress-container {
        margin: 30px 0;
        animation: progressSlide 0.8s ease 0.8s both;
        display: none;
      }

      @keyframes progressSlide {
        from {
          opacity: 0;
          transform: translateY(20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .progress-bar {
        height: 16px;
        background: var(--primary-bg);
        border-radius: 20px;
        overflow: hidden;
        margin-bottom: 15px;
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light);
        position: relative;
      }

      .progress-fill {
        height: 100%;
        background: var(--accent-gradient);
        width: 0%;
        transition: width 0.3s ease;
        border-radius: 20px;
        box-shadow: 0 2px 10px rgba(102, 126, 234, 0.3);
        position: relative;
        overflow: hidden;
      }

      .progress-fill::after {
        content: '';
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(
          90deg,
          transparent,
          rgba(255, 255, 255, 0.3),
          transparent
        );
        animation: progressShine 2s infinite;
      }

      @keyframes progressShine {
        0% {
          left: -100%;
        }
        100% {
          left: 100%;
        }
      }

      .progress-text {
        text-align: center;
        font-size: 1rem;
        color: var(--text-secondary);
        font-weight: 500;
      }

      .status-message {
        min-height: 35px;
        margin: 20px 0;
        color: var(--accent-color);
        font-weight: 600;
        font-size: 1.1rem;
        transition: all 0.3s ease;
        text-align: center;
      }

      .status-message.processing {
        animation: statusPulse 1.5s ease-in-out infinite;
      }

      @keyframes statusPulse {
        0%,
        100% {
          opacity: 1;
        }
        50% {
          opacity: 0.6;
        }
      }

      .error-message {
        color: var(--danger-color);
        margin: 20px 0;
        min-height: 35px;
        font-weight: 600;
        font-size: 1.1rem;
        animation: errorShake 0.5s ease;
        text-align: center;
      }

      @keyframes errorShake {
        0%,
        100% {
          transform: translateX(0);
        }
        25% {
          transform: translateX(-5px);
        }
        75% {
          transform: translateX(5px);
        }
      }

      .result-section {
        display: none;
        text-align: center;
        margin-top: 30px;
        padding: 25px;
        background: var(--primary-bg);
        border-radius: 25px;
        box-shadow: 10px 10px 20px var(--shadow-dark),
          -10px -10px 20px var(--shadow-light);
      }

      .result-video {
        width: 100%;
        max-width: 600px;
        margin: 20px auto;
        border-radius: 15px;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
      }

      .download-btn {
        background: linear-gradient(
          135deg,
          var(--success-color) 0%,
          #44a08d 100%
        );
        color: white;
        padding: 16px 32px;
        font-size: 1.1rem;
        margin: 20px 10px;
        border: none;
        border-radius: 15px;
        cursor: pointer;
        font-weight: 600;
        transition: all 0.3s ease;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2);
      }

      .download-btn:hover {
        transform: translateY(-3px);
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2);
      }

      .file-info {
        margin-top: 15px;
        padding: 15px;
        background: rgba(102, 126, 234, 0.1);
        border-radius: 15px;
        font-size: 0.9rem;
        color: var(--text-secondary);
      }

      .instructions {
        background: var(--primary-bg);
        border-radius: 25px;
        padding: 30px;
        margin-top: 40px;
        box-shadow: 10px 10px 20px var(--shadow-dark),
          -10px -10px 20px var(--shadow-light);
        animation: instructionsSlide 0.8s ease 1.4s both;
      }

      @keyframes instructionsSlide {
        from {
          opacity: 0;
          transform: translateY(30px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .instructions h3 {
        color: var(--accent-color);
        margin-bottom: 20px;
        font-size: 1.4rem;
        font-weight: 600;
      }

      .instructions ol {
        padding-left: 25px;
        margin-bottom: 20px;
      }

      .instructions li {
        margin-bottom: 12px;
        line-height: 1.7;
        color: var(--text-secondary);
        font-weight: 500;
      }

      .instructions p {
        color: var(--text-secondary);
        font-style: italic;
        font-weight: 500;
        padding: 15px;
        background: rgba(102, 126, 234, 0.1);
        border-radius: 15px;
        border-left: 4px solid var(--accent-color);
      }

      footer {
        margin-top: 40px;
        text-align: center;
        color: var(--text-secondary);
        font-size: 0.95rem;
        padding: 25px;
        background: var(--primary-bg);
        border-radius: 20px;
        box-shadow: 6px 6px 12px var(--shadow-dark),
          -6px -6px 12px var(--shadow-light);
        animation: footerFade 0.8s ease 1.6s both;
      }

      @keyframes footerFade {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }

      @media (max-width: 768px) {
        h1 {
          font-size: 2.5rem;
        }

        .container {
          padding: 20px;
        }

        .video-controls {
          flex-direction: column;
          gap: 10px;
        }

        .duration-input {
          justify-content: center;
        }
      }
    </style>
  </head>
  <body>
    <div class="container">
      <header>
        <h1>🎬 多视频合并长音频工具</h1>
        <p class="subtitle">将多个视频与一个长音频合并为完整视频</p>
      </header>

      <!-- 音频上传区域 -->
      <div class="audio-section">
        <h2 class="section-title" data-icon="🎵">音频文件</h2>
        <div class="panel">
          <div class="file-upload" id="audioUpload">
            <div class="file-upload-content">
              <span class="file-upload-icon">🎵</span>
              <div class="file-upload-text">选择音频文件</div>
              <div class="file-upload-subtext">点击选择或拖拽文件到此处</div>
              <div class="file-upload-formats">
                支持格式: MP3, WAV, AAC, M4A
              </div>
            </div>
            <input type="file" id="audioInput" accept="audio/*" />
          </div>
          <div id="audioPreview" style="display: none">
            <audio id="audioPlayer" controls class="audio-preview"></audio>
            <div id="audioInfo" class="file-info"></div>
          </div>
        </div>
      </div>

      <!-- 视频上传区域 -->
      <div class="video-section">
        <h2 class="section-title" data-icon="🎥">视频文件</h2>
        <div class="panel">
          <div class="file-upload" id="videoUpload">
            <div class="file-upload-content">
              <span class="file-upload-icon">🎥</span>
              <div class="file-upload-text">选择视频文件</div>
              <div class="file-upload-subtext">点击选择或拖拽文件到此处</div>
              <div class="file-upload-formats">
                支持格式: MP4, AVI, MOV, MKV
              </div>
            </div>
            <input type="file" id="videoInput" accept="video/*" multiple />
          </div>
          <div id="videoList" class="video-list"></div>
          <button class="add-video-btn" id="addVideoBtn" style="display: none">
            ➕ 添加更多视频
          </button>
        </div>
      </div>

      <!-- 处理按钮 -->
      <button class="btn-primary" id="processBtn" disabled>
        🚀 开始合并处理
      </button>

      <!-- 进度条 -->
      <div class="progress-container" id="progressContainer">
        <div class="progress-bar">
          <div class="progress-fill" id="progressFill"></div>
        </div>
        <div class="progress-text" id="progressText">准备中...</div>
      </div>

      <!-- 状态消息 -->
      <div class="status-message" id="statusMessage"></div>
      <div class="error-message" id="errorMessage"></div>

      <!-- 结果区域 -->
      <div class="result-section" id="resultSection">
        <h3>✅ 处理完成</h3>
        <video id="resultVideo" class="result-video" controls></video>
        <div>
          <button class="download-btn" id="downloadBtn">📥 下载视频</button>
        </div>
      </div>

      <!-- 使用说明 -->
      <div class="instructions">
        <h3>📋 使用说明</h3>
        <ol>
          <li>首先选择一个音频文件作为背景音乐</li>
          <li>然后选择多个视频文件，系统会按选择顺序排列</li>
          <li>
            为每个视频设置播放时长（秒），如果视频本身时长不够，会自动循环播放
          </li>
          <li>点击"开始合并处理"按钮开始处理</li>
          <li>处理完成后可以预览和下载合并后的视频</li>
        </ol>
        <p>
          💡
          提示：所有视频将按顺序连接，配合音频文件生成最终的合并视频。视频时长总和应与音频时长匹配以获得最佳效果。
        </p>
      </div>

      <footer>
        <p>
          🔧 基于 FFmpeg.wasm 技术 | 所有处理均在浏览器本地完成，保护您的隐私
        </p>
      </footer>
    </div>

    <script>
      const { createFFmpeg, fetchFile } = FFmpeg
      let ffmpeg = null
      let audioFile = null
      let videoFiles = []
      let videoCounter = 0

      // DOM 元素
      const audioUpload = document.getElementById('audioUpload')
      const audioInput = document.getElementById('audioInput')
      const audioPreview = document.getElementById('audioPreview')
      const audioPlayer = document.getElementById('audioPlayer')
      const audioInfo = document.getElementById('audioInfo')

      const videoUpload = document.getElementById('videoUpload')
      const videoInput = document.getElementById('videoInput')
      const videoList = document.getElementById('videoList')
      const addVideoBtn = document.getElementById('addVideoBtn')

      const processBtn = document.getElementById('processBtn')
      const progressContainer = document.getElementById('progressContainer')
      const progressFill = document.getElementById('progressFill')
      const progressText = document.getElementById('progressText')
      const statusMessage = document.getElementById('statusMessage')
      const errorMessage = document.getElementById('errorMessage')
      const resultSection = document.getElementById('resultSection')
      const resultVideo = document.getElementById('resultVideo')
      const downloadBtn = document.getElementById('downloadBtn')

      // 初始化 FFmpeg
      async function initFFmpeg() {
        if (!ffmpeg) {
          ffmpeg = createFFmpeg({
            log: true,
          })

          ffmpeg.setLogger(({ message }) => {
            const timeMatch = message.match(/time=(\d{2}:\d{2}:\d{2}\.\d{2})/)
            if (timeMatch) {
              const timeStr = timeMatch[1]
              updateProgress(50, `处理中... time=${timeStr} (参考时间)`)
            }
          })

          ffmpeg.setProgress(({ ratio }) => {
            const percent = Math.round(ratio * 100)
            // 只更新进度条，不更新文字
            progressFill.style.width = `${percent}%`
          })
        }

        if (!ffmpeg.isLoaded()) {
          showStatus('正在加载 FFmpeg...')
          await ffmpeg.load()
          showStatus('FFmpeg 加载完成')
        }
      }

      // 文件上传处理
      function setupFileUpload(uploadArea, input, callback) {
        // 点击上传
        uploadArea.addEventListener('click', (e) => {
          // 防止点击input元素时重复触发
          if (e.target === input) return
          e.preventDefault()
          input.click()
        })

        // 文件选择
        input.addEventListener('change', (e) => {
          callback(e.target.files)
        })

        // 拖拽上传
        uploadArea.addEventListener('dragover', (e) => {
          e.preventDefault()
          uploadArea.classList.add('dragover')
        })

        uploadArea.addEventListener('dragleave', () => {
          uploadArea.classList.remove('dragover')
        })

        uploadArea.addEventListener('drop', (e) => {
          e.preventDefault()
          uploadArea.classList.remove('dragover')
          callback(e.dataTransfer.files)
        })
      }

      // 处理音频文件
      function handleAudioFiles(files) {
        if (files.length === 0) return

        const file = files[0]
        if (!file.type.startsWith('audio/')) {
          showError('请选择有效的音频文件')
          return
        }

        audioFile = file
        const url = URL.createObjectURL(file)
        audioPlayer.src = url
        audioPreview.style.display = 'block'

        audioInfo.innerHTML = `
          <strong>文件名:</strong> ${file.name}<br>
          <strong>大小:</strong> ${(file.size / 1024 / 1024).toFixed(2)} MB<br>
          <strong>类型:</strong> ${file.type}
        `

        checkCanProcess()
      }

      // 处理视频文件
      function handleVideoFiles(files) {
        for (let file of files) {
          if (!file.type.startsWith('video/')) {
            showError('请选择有效的视频文件')
            continue
          }

          addVideoToList(file)
        }

        addVideoBtn.style.display = videoFiles.length > 0 ? 'block' : 'none'
        checkCanProcess()
      }

      // 添加视频到列表
      function addVideoToList(file) {
        const videoId = `video_${++videoCounter}`
        const videoItem = {
          id: videoId,
          file: file,
          duration: 10, // 默认10秒
        }

        videoFiles.push(videoItem)

        const videoElement = document.createElement('div')
        videoElement.className = 'video-item'
        videoElement.innerHTML = `
          <div class="video-header">
            <div class="video-name">${file.name}</div>
            <div class="video-controls">
              <div class="duration-input">
                <label>时长:</label>
                <input type="number" value="10" min="1" max="3600" id="duration_${videoId}">
                <span>秒</span>
              </div>
              <button class="remove-btn" onclick="removeVideo('${videoId}')">删除</button>
            </div>
          </div>
          <video class="video-preview" controls>
            <source src="${URL.createObjectURL(file)}" type="${file.type}">
          </video>
        `

        videoList.appendChild(videoElement)

        // 监听时长变化
        document
          .getElementById(`duration_${videoId}`)
          .addEventListener('change', (e) => {
            const video = videoFiles.find((v) => v.id === videoId)
            if (video) {
              video.duration = parseInt(e.target.value) || 10
            }
          })
      }

      // 删除视频
      function removeVideo(videoId) {
        videoFiles = videoFiles.filter((v) => v.id !== videoId)
        const videoElement = document
          .querySelector(`#duration_${videoId}`)
          .closest('.video-item')
        videoElement.remove()

        addVideoBtn.style.display = videoFiles.length > 0 ? 'block' : 'none'
        checkCanProcess()
      }

      // 检查是否可以开始处理
      function checkCanProcess() {
        processBtn.disabled = !audioFile || videoFiles.length === 0
      }

      // 处理合并
      async function processVideoMerge() {
        try {
          await initFFmpeg()

          showProgress()
          updateProgress(10, '准备文件...')

          // 写入音频文件
          const audioName = `audio.${audioFile.name.split('.').pop()}`
          ffmpeg.FS('writeFile', audioName, await fetchFile(audioFile))

          // 写入视频文件并创建循环视频
          const processedVideos = []
          for (let i = 0; i < videoFiles.length; i++) {
            const video = videoFiles[i]
            const inputName = `input_${i}.${video.file.name.split('.').pop()}`
            const outputName = `processed_${i}.mp4`

            ffmpeg.FS('writeFile', inputName, await fetchFile(video.file))

            updateProgress(
              20 + (i * 30) / videoFiles.length,
              `处理视频 ${i + 1}/${videoFiles.length}...`
            )

            // 创建循环视频以达到指定时长
            await ffmpeg.run(
              '-stream_loop',
              '-1',
              '-i',
              inputName,
              '-t',
              video.duration.toString(),
              '-c:v',
              'libx264',
              '-c:a',
              'aac',
              '-y',
              outputName
            )

            processedVideos.push(outputName)
          }

          updateProgress(60, '合并视频...')

          // 创建视频列表文件
          const videoListContent = processedVideos
            .map((name) => `file '${name}'`)
            .join('\n')
          ffmpeg.FS('writeFile', 'videolist.txt', videoListContent)

          // 合并所有视频
          const mergedVideoName = 'merged_video.mp4'
          await ffmpeg.run(
            '-f',
            'concat',
            '-safe',
            '0',
            '-i',
            'videolist.txt',
            '-c',
            'copy',
            '-y',
            mergedVideoName
          )

          updateProgress(80, '添加音频...')

          // 将音频添加到合并后的视频
          const finalOutputName = 'final_output.mp4'
          await ffmpeg.run(
            '-i',
            mergedVideoName,
            '-i',
            audioName,
            '-c:v',
            'copy',
            '-c:a',
            'aac',
            '-map',
            '0:v:0',
            '-map',
            '1:a:0',
            '-shortest',
            '-y',
            finalOutputName
          )

          updateProgress(95, '准备下载文件...')

          // 读取输出文件
          const outputData = ffmpeg.FS('readFile', finalOutputName)

          // 创建下载链接和预览
          const blob = new Blob([outputData.buffer], { type: 'video/mp4' })
          const url = URL.createObjectURL(blob)

          // 设置预览和下载
          resultVideo.src = url
          downloadBtn.onclick = () => {
            const a = document.createElement('a')
            a.href = url
            a.download = 'merged_video.mp4'
            a.click()
          }

          // 显示结果
          hideProgress()
          resultSection.style.display = 'block'
          showStatus('处理完成！')

          // 清理临时文件
          const filesToClean = [
            audioName,
            ...processedVideos,
            'videolist.txt',
            mergedVideoName,
            finalOutputName,
          ]
          filesToClean.forEach((filename) => {
            try {
              ffmpeg.FS('unlink', filename)
            } catch (e) {
              // 忽略清理错误
            }
          })
        } catch (error) {
          console.error('处理错误:', error)
          showError('处理失败: ' + error.message)
          hideProgress()
        }
      }

      // 更新进度
      function updateProgress(percent, text) {
        progressFill.style.width = `${percent}%`
        progressText.textContent = text
      }

      // 显示进度条
      function showProgress() {
        progressContainer.style.display = 'block'
        statusMessage.classList.add('processing')
      }

      // 隐藏进度条
      function hideProgress() {
        progressContainer.style.display = 'none'
        statusMessage.classList.remove('processing')
      }

      // 显示状态消息
      function showStatus(message) {
        statusMessage.textContent = message
        errorMessage.textContent = ''
      }

      // 显示错误消息
      function showError(message) {
        errorMessage.textContent = message
        statusMessage.textContent = ''
      }

      // 重置所有状态
      function resetAll() {
        audioFile = null
        videoFiles = []
        videoCounter = 0
        audioPreview.style.display = 'none'
        videoList.innerHTML = ''
        addVideoBtn.style.display = 'none'
        resultSection.style.display = 'none'
        hideProgress()
        showStatus('')
        checkCanProcess()
      }

      // 初始化事件监听器
      function initEventListeners() {
        setupFileUpload(audioUpload, audioInput, handleAudioFiles)
        setupFileUpload(videoUpload, videoInput, handleVideoFiles)

        addVideoBtn.addEventListener('click', (e) => {
          e.stopPropagation()
          videoInput.click()
        })

        processBtn.addEventListener('click', processVideoMerge)
      }

      // 页面加载完成后初始化
      document.addEventListener('DOMContentLoaded', () => {
        initEventListeners()
      })

      // 全局函数供HTML调用
      window.removeVideo = removeVideo
    </script>
  </body>
</html>
